using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using System.Text.Json;
using JGSY.CMS.LowCode.Platform.Application.Interfaces;

namespace JGSY.CMS.LowCode.Platform.Infrastructure.Middleware
{
    /// <summary>
    /// 审计日志记录中间件
    /// 记录需要审计的业务操作和数据变更
    /// </summary>
    public class AuditLoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<AuditLoggingMiddleware> _logger;

        // 需要审计的端点配置
        private static readonly HashSet<string> AuditEndpoints = new(StringComparer.OrdinalIgnoreCase)
        {
            "/api/users",
            "/api/roles", 
            "/api/permissions",
            "/api/departments",
            "/api/teams",
            "/api/audit",
            "/api/security",
            "/api/system/settings",
            "/api/categories",
            "/api/articles",
            "/api/pages",
            "/api/templates",
            "/api/media",
            "/api/forms",
            "/api/workflows",
            "/api/products",
            "/api/content"
        };

        // 审计操作类型映射
        private static readonly Dictionary<string, string> AuditActionMapping = new()
        {
            { "POST", "Create" },
            { "PUT", "Update" },
            { "PATCH", "PartialUpdate" },
            { "DELETE", "Delete" },
            { "GET", "Read" }
        };

        // 敏感字段配置
        private static readonly HashSet<string> SensitiveFields = new(StringComparer.OrdinalIgnoreCase)
        {
            "password", "pwd", "passwd", "secret", "token", "key", "apikey",
            "creditcard", "ssn", "sin", "bankaccount", "pin", "hash"
        };

        public AuditLoggingMiddleware(RequestDelegate next, ILogger<AuditLoggingMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            // 检查是否需要审计
            if (!ShouldAuditRequest(context))
            {
                await _next(context);
                return;
            }

            var auditInfo = await PrepareAuditInfoAsync(context);
            var originalBodyStream = context.Response.Body;
            
            try
            {
                // 捕获响应内容
                using var responseMemoryStream = new MemoryStream();
                context.Response.Body = responseMemoryStream;

                await _next(context);

                // 处理响应
                responseMemoryStream.Seek(0, SeekOrigin.Begin);
                var responseBody = await new StreamReader(responseMemoryStream).ReadToEndAsync();
                responseMemoryStream.Seek(0, SeekOrigin.Begin);
                await responseMemoryStream.CopyToAsync(originalBodyStream);

                // 完善审计信息
                auditInfo.ResponseData = ProcessResponseData(responseBody, context.Response.StatusCode);
                auditInfo.Success = context.Response.StatusCode < 400;
                auditInfo.CompletedAt = DateTime.UtcNow;

                // 记录审计日志
                await LogAuditAsync(context, auditInfo);
            }
            catch (Exception ex)
            {
                auditInfo.Success = false;
                auditInfo.ErrorMessage = ex.Message;
                auditInfo.CompletedAt = DateTime.UtcNow;
                
                // 记录失败的审计日志
                await LogAuditAsync(context, auditInfo);
                throw;
            }
            finally
            {
                context.Response.Body = originalBodyStream;
            }
        }

        private bool ShouldAuditRequest(HttpContext context)
        {
            var path = context.Request.Path.Value ?? "";
            var method = context.Request.Method;

            // 跳过GET请求（除非是特定的查询操作）
            if (method.Equals("GET", StringComparison.OrdinalIgnoreCase) && !IsAuditableGetOperation(path))
            {
                return false;
            }

            // 检查是否匹配审计端点
            return AuditEndpoints.Any(endpoint => path.StartsWith(endpoint, StringComparison.OrdinalIgnoreCase));
        }

        private bool IsAuditableGetOperation(string path)
        {
            // 某些GET操作也需要审计，如导出、报告等
            var auditableGetPaths = new[]
            {
                "/api/audit/export",
                "/api/users/export", 
                "/api/reports/",
                "/api/security/logs"
            };

            return auditableGetPaths.Any(auditPath => path.StartsWith(auditPath, StringComparison.OrdinalIgnoreCase));
        }

        private async Task<AuditInfo> PrepareAuditInfoAsync(HttpContext context)
        {
            var auditInfo = new AuditInfo
            {
                StartedAt = DateTime.UtcNow,
                RequestPath = context.Request.Path.Value ?? "",
                HttpMethod = context.Request.Method,
                Action = DetermineAuditAction(context),
                EntityType = DetermineEntityType(context.Request.Path),
                UserId = GetUserIdFromContext(context),
                UserName = GetUserNameFromContext(context),
                IpAddress = GetClientIpAddress(context),
                UserAgent = context.Request.Headers.UserAgent.ToString(),
                TraceId = context.TraceIdentifier,
                TenantId = GetTenantIdFromContext(context),
                CorrelationId = GetCorrelationIdFromContext(context)
            };

            // 捕获请求数据
            auditInfo.RequestData = await CaptureRequestDataAsync(context);

            // 捕获查询参数
            if (context.Request.Query.Any())
            {
                auditInfo.QueryParameters = context.Request.Query.ToDictionary(q => q.Key, q => q.Value.ToString());
            }

            // 捕获路由参数
            if (context.Request.RouteValues.Any())
            {
                auditInfo.RouteValues = context.Request.RouteValues.ToDictionary(r => r.Key, r => r.Value?.ToString());
            }

            return auditInfo;
        }

        private string DetermineAuditAction(HttpContext context)
        {
            var method = context.Request.Method.ToUpper();
            var path = context.Request.Path.Value ?? "";

            // 特殊操作映射
            if (path.Contains("/login", StringComparison.OrdinalIgnoreCase))
                return "Login";
            else if (path.Contains("/logout", StringComparison.OrdinalIgnoreCase))
                return "Logout";
            else if (path.Contains("/export", StringComparison.OrdinalIgnoreCase))
                return "Export";
            else if (path.Contains("/import", StringComparison.OrdinalIgnoreCase))
                return "Import";
            else if (path.Contains("/reset", StringComparison.OrdinalIgnoreCase))
                return "Reset";
            else if (path.Contains("/activate", StringComparison.OrdinalIgnoreCase))
                return "Activate";
            else if (path.Contains("/deactivate", StringComparison.OrdinalIgnoreCase))
                return "Deactivate";

            // 默认HTTP方法映射
            return AuditActionMapping.GetValueOrDefault(method, "Unknown");
        }

        private string DetermineEntityType(PathString path)
        {
            var pathValue = path.Value ?? "";
            
            if (pathValue.Contains("/users", StringComparison.OrdinalIgnoreCase))
                return "User";
            else if (pathValue.Contains("/roles", StringComparison.OrdinalIgnoreCase))
                return "Role";
            else if (pathValue.Contains("/permissions", StringComparison.OrdinalIgnoreCase))
                return "Permission";
            else if (pathValue.Contains("/departments", StringComparison.OrdinalIgnoreCase))
                return "Department";
            else if (pathValue.Contains("/teams", StringComparison.OrdinalIgnoreCase))
                return "Team";
            else if (pathValue.Contains("/articles", StringComparison.OrdinalIgnoreCase))
                return "Article";
            else if (pathValue.Contains("/categories", StringComparison.OrdinalIgnoreCase))
                return "Category";
            else if (pathValue.Contains("/pages", StringComparison.OrdinalIgnoreCase))
                return "Page";
            else if (pathValue.Contains("/templates", StringComparison.OrdinalIgnoreCase))
                return "Template";
            else if (pathValue.Contains("/media", StringComparison.OrdinalIgnoreCase))
                return "Media";
            else if (pathValue.Contains("/forms", StringComparison.OrdinalIgnoreCase))
                return "Form";
            else if (pathValue.Contains("/workflows", StringComparison.OrdinalIgnoreCase))
                return "Workflow";
            else if (pathValue.Contains("/products", StringComparison.OrdinalIgnoreCase))
                return "Product";
            else if (pathValue.Contains("/content", StringComparison.OrdinalIgnoreCase))
                return "Content";
            else if (pathValue.Contains("/system", StringComparison.OrdinalIgnoreCase))
                return "SystemSetting";
            else if (pathValue.Contains("/audit", StringComparison.OrdinalIgnoreCase))
                return "AuditLog";
            else if (pathValue.Contains("/security", StringComparison.OrdinalIgnoreCase))
                return "Security";
            else
                return "Unknown";
        }

        private async Task<Dictionary<string, object>?> CaptureRequestDataAsync(HttpContext context)
        {
            if (context.Request.ContentLength > 0 && context.Request.ContentLength < 51200) // 限制50KB
            {
                try
                {
                    context.Request.EnableBuffering();
                    context.Request.Body.Position = 0;
                    
                    using var reader = new StreamReader(context.Request.Body, Encoding.UTF8, leaveOpen: true);
                    var requestBody = await reader.ReadToEndAsync();
                    context.Request.Body.Position = 0;

                    if (!string.IsNullOrEmpty(requestBody))
                    {
                        // 尝试解析JSON并脱敏敏感字段
                        return ProcessRequestData(requestBody);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "捕获请求数据时发生错误");
                    return new Dictionary<string, object> { ["Error"] = $"捕获请求数据失败: {ex.Message}" };
                }
            }

            return null;
        }

        private Dictionary<string, object> ProcessRequestData(string requestBody)
        {
            try
            {
                // 尝试解析为JSON
                var jsonDocument = JsonDocument.Parse(requestBody);
                var processedData = ProcessJsonElement(jsonDocument.RootElement);
                
                return new Dictionary<string, object>
                {
                    ["Data"] = processedData,
                    ["Size"] = requestBody.Length
                };
            }
            catch
            {
                // 如果不是JSON，直接存储（但要脱敏）
                return new Dictionary<string, object>
                {
                    ["RawData"] = SanitizeSensitiveData(requestBody),
                    ["Size"] = requestBody.Length,
                    ["Type"] = "Non-JSON"
                };
            }
        }

        private object ProcessJsonElement(JsonElement element)
        {
            switch (element.ValueKind)
            {
                case JsonValueKind.Object:
                    var obj = new Dictionary<string, object>();
                    foreach (var prop in element.EnumerateObject())
                    {
                        var value = ProcessJsonElement(prop.Value);
                        // 脱敏敏感字段
                        if (IsSensitiveField(prop.Name))
                        {
                            obj[prop.Name] = "[REDACTED]";
                        }
                        else
                        {
                            obj[prop.Name] = value;
                        }
                    }
                    return obj;

                case JsonValueKind.Array:
                    var array = new List<object>();
                    foreach (var item in element.EnumerateArray())
                    {
                        array.Add(ProcessJsonElement(item));
                    }
                    return array;

                case JsonValueKind.String:
                    return element.GetString() ?? "";
                case JsonValueKind.Number:
                    return element.TryGetInt64(out var longVal) ? longVal : element.GetDouble();
                case JsonValueKind.True:
                case JsonValueKind.False:
                    return element.GetBoolean();
                case JsonValueKind.Null:
                    return "null";
                default:
                    return element.GetRawText();
            }
        }

        private Dictionary<string, object> ProcessResponseData(string responseBody, int statusCode)
        {
            var responseData = new Dictionary<string, object>
            {
                ["StatusCode"] = statusCode,
                ["Size"] = responseBody?.Length ?? 0,
                ["HasContent"] = !string.IsNullOrEmpty(responseBody)
            };

            // 只记录小的响应内容，避免记录大量数据
            if (!string.IsNullOrEmpty(responseBody) && responseBody.Length <= 2048) // 限制2KB
            {
                try
                {
                    var jsonDoc = JsonDocument.Parse(responseBody);
                    responseData["Data"] = ProcessJsonElement(jsonDoc.RootElement);
                }
                catch
                {
                    // 非JSON响应
                    responseData["Data"] = SanitizeSensitiveData(responseBody);
                }
            }
            else if (!string.IsNullOrEmpty(responseBody))
            {
                responseData["Data"] = "[响应内容过大，已省略]";
            }

            return responseData;
        }

        private bool IsSensitiveField(string fieldName)
        {
            return SensitiveFields.Contains(fieldName);
        }

        private string SanitizeSensitiveData(string input)
        {
            // 简单的脱敏处理
            foreach (var sensitiveField in SensitiveFields)
            {
                var pattern = $@"""{sensitiveField}""\s*:\s*""[^""]*""";
                input = System.Text.RegularExpressions.Regex.Replace(
                    input, 
                    pattern, 
                    $@"""{sensitiveField}"":""[REDACTED]""", 
                    System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            }
            return input;
        }

        private async Task LogAuditAsync(HttpContext context, AuditInfo auditInfo)
        {
            try
            {
                // 控制台审计日志
                LogAuditToConsole(auditInfo);

                // 数据库审计日志记录（现在可以启用）
                var auditLogService = context.RequestServices.GetService<IAuditLogAppService>();
                if (auditLogService != null)
                {
                    var additionalData = new Dictionary<string, object>
                    {
                        ["HttpMethod"] = auditInfo.HttpMethod ?? "",
                        ["RequestPath"] = auditInfo.RequestPath ?? "",                          // filepath: 
["QueryParameters"] = auditInfo.QueryParameters?.ToDictionary(kvp => kvp.Key, kvp => (object)kvp.Value) ?? new Dictionary<string, object>(),
["RouteValues"] = auditInfo.RouteValues?.ToDictionary(kvp => kvp.Key, kvp => (object)(kvp.Value ?? "")) ?? new Dictionary<string, object>(),                      ["QueryParameters"] = auditInfo.QueryParameters?.ToDictionary(kvp => kvp.Key, kvp => (object)kvp.Value) ?? new Dictionary<string, object>(),
                        ["RouteValues"] = auditInfo.RouteValues?.Where(kvp => kvp.Value != null).ToDictionary(kvp => kvp.Key, kvp => (object)kvp.Value!) ?? new Dictionary<string, object>(),
                        ["RequestData"] = auditInfo.RequestData ?? new Dictionary<string, object>(),
                        ["ResponseData"] = auditInfo.ResponseData ?? new Dictionary<string, object>(),
                        ["UserAgent"] = auditInfo.UserAgent ?? "",
                        ["IpAddress"] = auditInfo.IpAddress ?? "",
                        ["TraceId"] = auditInfo.TraceId ?? "",
                        ["Duration"] = auditInfo.CompletedAt?.Subtract(auditInfo.StartedAt).TotalMilliseconds ?? 0
                    };

                    await auditLogService.LogOperationAsync(
                        operationType: auditInfo.Action,
                        content: $"{auditInfo.HttpMethod} {auditInfo.RequestPath}",
                        userId: auditInfo.UserId,
                        userName: auditInfo.UserName,
                        ip: auditInfo.IpAddress,
                        resource: auditInfo.EntityType,
                        result: auditInfo.Success ? "Success" : "Failed",
                        exception: auditInfo.ErrorMessage);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "记录审计日志时发生错误");
            }
        }

        private void LogAuditToConsole(AuditInfo auditInfo)
        {
            var logLevel = auditInfo.Success ? LogLevel.Information : LogLevel.Warning;
            
            var message = $"审计日志: {auditInfo.Action} {auditInfo.EntityType} | " +
                         $"用户: {auditInfo.UserName}({auditInfo.UserId}) | " +
                         $"路径: {auditInfo.HttpMethod} {auditInfo.RequestPath} | " +
                         $"状态: {(auditInfo.Success ? "成功" : "失败")} | " +
                         $"IP: {auditInfo.IpAddress} | " +
                         $"TraceId: {auditInfo.TraceId}";

            if (!string.IsNullOrEmpty(auditInfo.ErrorMessage))
            {
                message += $" | 错误: {auditInfo.ErrorMessage}";
            }

            _logger.Log(logLevel, message);
        }

        private string? ExtractEntityIdFromRoute(Dictionary<string, string?>? routeValues)
        {
            if (routeValues == null) return null;
            
            // 尝试从路由值中提取实体ID
            var idKeys = new[] { "id", "userId", "roleId", "articleId", "categoryId", "pageId" };
            
            foreach (var key in idKeys)
            {
                if (routeValues.TryGetValue(key, out var value) && !string.IsNullOrEmpty(value))
                {
                    return value;
                }
            }
            
            return null;
        }

        #region 辅助方法

        private string? GetTenantIdFromContext(HttpContext context)
        {
            return context.Request.Headers.TryGetValue("X-Tenant-ID", out var tenantValues) ? 
                tenantValues.FirstOrDefault() :
                context.User?.FindFirst("tenant_id")?.Value;
        }

        private string? GetUserIdFromContext(HttpContext context)
        {
            return context.User?.FindFirst("sub")?.Value ?? 
                context.User?.FindFirst("user_id")?.Value ??
                context.User?.Identity?.Name;
        }

        private string? GetUserNameFromContext(HttpContext context)
        {
            return context.User?.FindFirst("name")?.Value ?? 
                context.User?.FindFirst("username")?.Value;
        }

        private string? GetCorrelationIdFromContext(HttpContext context)
        {
            return context.Request.Headers.TryGetValue("X-Correlation-ID", out var correlationValues) ? 
                correlationValues.FirstOrDefault() : 
                context.TraceIdentifier;
        }

        private string GetClientIpAddress(HttpContext context)
        {
            var xForwardedFor = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
            if (!string.IsNullOrEmpty(xForwardedFor))
            {
                var ips = xForwardedFor.Split(',');
                return ips[0].Trim();
            }

            var xRealIp = context.Request.Headers["X-Real-IP"].FirstOrDefault();
            if (!string.IsNullOrEmpty(xRealIp))
            {
                return xRealIp;
            }

            return context.Connection.RemoteIpAddress?.ToString() ?? "Unknown";
        }

        #endregion
    }

    /// <summary>
    /// 审计信息
    /// </summary>
    public class AuditInfo
    {
        public DateTime StartedAt { get; set; }
        public DateTime? CompletedAt { get; set; }
        public string RequestPath { get; set; } = "";
        public string HttpMethod { get; set; } = "";
        public string Action { get; set; } = "";
        public string EntityType { get; set; } = "";
        public string? UserId { get; set; }
        public string? UserName { get; set; }
        public string IpAddress { get; set; } = "";
        public string UserAgent { get; set; } = "";
        public string TraceId { get; set; } = "";
        public string? TenantId { get; set; }
        public string? CorrelationId { get; set; }
        public Dictionary<string, string>? QueryParameters { get; set; }
        public Dictionary<string, string?>? RouteValues { get; set; }
        public Dictionary<string, object>? RequestData { get; set; }
        public Dictionary<string, object>? ResponseData { get; set; }
        public bool Success { get; set; }
        public string? ErrorMessage { get; set; }
    }
}
